www.gusucode.com > Piwik 网站流量统计系统 v2.9.1PHP源码程序 > Piwik 网站流量统计系统 v2.9.1/piwik/piwik/core/Columns/Updater.php

    <?php
/**
 * Piwik - free/libre analytics platform
 *
 * @link http://piwik.org
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 *
 */
namespace Piwik\Columns;
use Piwik\Common;
use Piwik\DbHelper;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Plugin\Dimension\ConversionDimension;
use Piwik\Db;
use Piwik\Updater as PiwikUpdater;
use Piwik\Cache\PersistentCache;
use Piwik\Filesystem;

/**
 * Class that handles dimension updates
 */
class Updater extends \Piwik\Updates
{
    /**
     * @var Updater
     */
    private static $updater;

    /**
     * Return SQL to be executed in this update
     *
     * @return array(
     *              'ALTER .... ' => '1234', // if the query fails, it will be ignored if the error code is 1234
     *              'ALTER .... ' => false,  // if an error occurs, the update will stop and fail
     *                                       // and user will have to manually run the query
     *         )
     */
    public static function getSql()
    {
        $sqls = array();

        $changingColumns = self::getUpdates();

        foreach ($changingColumns as $table => $columns) {
            if (empty($columns) || !is_array($columns)) {
                continue;
            }

            $sqls["ALTER TABLE `" . Common::prefixTable($table) . "` " . implode(', ', $columns)] = false;
        }

        return $sqls;
    }

    /**
     * Incremental version update
     */
    public static function update()
    {
        foreach (self::getSql() as $sql => $errorCode) {
            try {
                Db::exec($sql);
            } catch (\Exception $e) {
                if (!Db::get()->isErrNo($e, '1091') && !Db::get()->isErrNo($e, '1060')) {
                    PiwikUpdater::handleQueryError($e, $sql, false, __FILE__);
                }
            }
        }
    }

    public static function setUpdater($updater)
    {
        self::$updater = $updater;
    }

    private static function hasComponentNewVersion($component)
    {
        return empty(self::$updater) || self::$updater->hasNewVersion($component);
    }

    private static function getUpdates()
    {
        $visitColumns      = DbHelper::getTableColumns(Common::prefixTable('log_visit'));
        $actionColumns     = DbHelper::getTableColumns(Common::prefixTable('log_link_visit_action'));
        $conversionColumns = DbHelper::getTableColumns(Common::prefixTable('log_conversion'));

        $changingColumns = array();

        foreach (self::getVisitDimensions() as $dimension) {
            $updates         = self::getUpdatesForDimension($dimension, 'log_visit.', $visitColumns, $conversionColumns);
            $changingColumns = self::mixinUpdates($changingColumns, $updates);
        }

        foreach (self::getActionDimensions() as $dimension) {
            $updates         = self::getUpdatesForDimension($dimension, 'log_link_visit_action.', $actionColumns);
            $changingColumns = self::mixinUpdates($changingColumns, $updates);
        }

        foreach (self::getConversionDimensions() as $dimension) {
            $updates         = self::getUpdatesForDimension($dimension, 'log_conversion.', $conversionColumns);
            $changingColumns = self::mixinUpdates($changingColumns, $updates);
        }

        return $changingColumns;
    }

    /**
     * @param ActionDimension|ConversionDimension|VisitDimension $dimension
     * @param string $componentPrefix
     * @param array $existingColumnsInDb
     * @param array $conversionColumns
     * @return array
     */
    private static function getUpdatesForDimension($dimension, $componentPrefix, $existingColumnsInDb, $conversionColumns = array())
    {
        $column = $dimension->getColumnName();
        $componentName = $componentPrefix . $column;

        if (!self::hasComponentNewVersion($componentName)) {
            return array();
        }

        if (array_key_exists($column, $existingColumnsInDb)) {
            if ($dimension instanceof VisitDimension) {
                $sqlUpdates = $dimension->update($conversionColumns);
            } else {
                $sqlUpdates = $dimension->update();
            }
        } else {
            $sqlUpdates = $dimension->install();
        }

        return $sqlUpdates;
    }

    private static function mixinUpdates($changingColumns, $updatesFromDimension)
    {
        if (!empty($updatesFromDimension)) {
            foreach ($updatesFromDimension as $table => $col) {
                if (empty($changingColumns[$table])) {
                    $changingColumns[$table] = $col;
                } else {
                    $changingColumns[$table] = array_merge($changingColumns[$table], $col);
                }
            }
        }

        return $changingColumns;
    }

    public static function getAllVersions()
    {
        // to avoid having to load all dimensions on each request we check if there were any changes on the file system
        // can easily save > 100ms for each request
        $cachedTimes  = self::getCachedDimensionFileChanges();
        $currentTimes = self::getCurrentDimensionFileChanges();
        $diff         = array_diff_assoc($currentTimes, $cachedTimes);

        if (empty($diff)) {
            return array();
        }

        $versions = array();

        $visitColumns      = DbHelper::getTableColumns(Common::prefixTable('log_visit'));
        $actionColumns     = DbHelper::getTableColumns(Common::prefixTable('log_link_visit_action'));
        $conversionColumns = DbHelper::getTableColumns(Common::prefixTable('log_conversion'));

        foreach (self::getVisitDimensions() as $dimension) {
            $versions = self::mixinVersions($dimension, 'log_visit.', $visitColumns, $versions);
        }

        foreach (self::getActionDimensions() as $dimension) {
            $versions = self::mixinVersions($dimension, 'log_link_visit_action.', $actionColumns, $versions);
        }

        foreach (self::getConversionDimensions() as $dimension) {
            $versions = self::mixinVersions($dimension, 'log_conversion.', $conversionColumns, $versions);
        }

        return $versions;
    }

    /**
     * @param ActionDimension|ConversionDimension|VisitDimension $dimension
     * @param string $componentPrefix
     * @param array $columns
     * @param array $versions
     * @return array The modified versions array
     */
    private static function mixinVersions($dimension, $componentPrefix, $columns, $versions)
    {
        $columnName = $dimension->getColumnName();

        if (!$columnName || !$dimension->hasColumnType()) {
            return $versions;
        }

        $component = $componentPrefix . $columnName;
        $version   = $dimension->getVersion();

        if (array_key_exists($columnName, $columns)
            && false === PiwikUpdater::getCurrentRecordedComponentVersion($component)
            && self::wasDimensionMovedFromCoreToPlugin($component, $version)) {
            PiwikUpdater::recordComponentSuccessfullyUpdated($component, $version);
            return $versions;
        }

        $versions[$component] = $version;

        return $versions;
    }

    public static function isDimensionComponent($name)
    {
        return 0 === strpos($name, 'log_visit.')
            || 0 === strpos($name, 'log_conversion.')
            || 0 === strpos($name, 'log_conversion_item.')
            || 0 === strpos($name, 'log_link_visit_action.');
    }

    public static function wasDimensionMovedFromCoreToPlugin($name, $version)
    {
        $dimensions = array (
            'log_visit.config_resolution' => 'VARCHAR(9) NOT NULL',
            'log_visit.config_device_brand' => 'VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL',
            'log_visit.config_device_model' => 'VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL',
            'log_visit.config_windowsmedia' => 'TINYINT(1) NOT NULL',
            'log_visit.config_silverlight' => 'TINYINT(1) NOT NULL',
            'log_visit.config_java' => 'TINYINT(1) NOT NULL',
            'log_visit.config_gears' => 'TINYINT(1) NOT NULL',
            'log_visit.config_pdf' => 'TINYINT(1) NOT NULL',
            'log_visit.config_quicktime' => 'TINYINT(1) NOT NULL',
            'log_visit.config_realplayer' => 'TINYINT(1) NOT NULL',
            'log_visit.config_device_type' => 'TINYINT( 100 ) NULL DEFAULT NULL',
            'log_visit.visitor_localtime' => 'TIME NOT NULL',
            'log_visit.location_region' => 'char(2) DEFAULT NULL1',
            'log_visit.visitor_days_since_last' => 'SMALLINT(5) UNSIGNED NOT NULL',
            'log_visit.location_longitude' => 'float(10, 6) DEFAULT NULL1',
            'log_visit.visit_total_events' => 'SMALLINT(5) UNSIGNED NOT NULL',
            'log_visit.config_os_version' => 'VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL',
            'log_visit.location_city' => 'varchar(255) DEFAULT NULL1',
            'log_visit.location_country' => 'CHAR(3) NOT NULL1',
            'log_visit.location_latitude' => 'float(10, 6) DEFAULT NULL1',
            'log_visit.config_flash' => 'TINYINT(1) NOT NULL',
            'log_visit.config_director' => 'TINYINT(1) NOT NULL',
            'log_visit.visit_total_time' => 'SMALLINT(5) UNSIGNED NOT NULL',
            'log_visit.visitor_count_visits' => 'SMALLINT(5) UNSIGNED NOT NULL1',
            'log_visit.visit_entry_idaction_name' => 'INTEGER(11) UNSIGNED NOT NULL',
            'log_visit.visit_entry_idaction_url' => 'INTEGER(11) UNSIGNED NOT NULL',
            'log_visit.visitor_returning' => 'TINYINT(1) NOT NULL1',
            'log_visit.visitor_days_since_order' => 'SMALLINT(5) UNSIGNED NOT NULL1',
            'log_visit.visit_goal_buyer' => 'TINYINT(1) NOT NULL',
            'log_visit.visit_first_action_time' => 'DATETIME NOT NULL',
            'log_visit.visit_goal_converted' => 'TINYINT(1) NOT NULL',
            'log_visit.visitor_days_since_first' => 'SMALLINT(5) UNSIGNED NOT NULL1',
            'log_visit.visit_exit_idaction_name' => 'INTEGER(11) UNSIGNED NOT NULL',
            'log_visit.visit_exit_idaction_url' => 'INTEGER(11) UNSIGNED NULL DEFAULT 0',
            'log_visit.config_browser_version' => 'VARCHAR(20) NOT NULL',
            'log_visit.config_browser_name' => 'VARCHAR(10) NOT NULL',
            'log_visit.config_browser_engine' => 'VARCHAR(10) NOT NULL',
            'log_visit.location_browser_lang' => 'VARCHAR(20) NOT NULL',
            'log_visit.config_os' => 'CHAR(3) NOT NULL',
            'log_visit.config_cookie' => 'TINYINT(1) NOT NULL',
            'log_visit.referer_url' => 'TEXT NOT NULL',
            'log_visit.visit_total_searches' => 'SMALLINT(5) UNSIGNED NOT NULL',
            'log_visit.visit_total_actions' => 'SMALLINT(5) UNSIGNED NOT NULL',
            'log_visit.referer_keyword' => 'VARCHAR(255) NULL1',
            'log_visit.referer_name' => 'VARCHAR(70) NULL1',
            'log_visit.referer_type' => 'TINYINT(1) UNSIGNED NULL1',
            'log_visit.user_id' => 'VARCHAR(200) NULL',
            'log_link_visit_action.idaction_name' => 'INTEGER(10) UNSIGNED',
            'log_link_visit_action.idaction_url' => 'INTEGER(10) UNSIGNED DEFAULT NULL',
            'log_link_visit_action.server_time' => 'DATETIME NOT NULL',
            'log_link_visit_action.time_spent_ref_action' => 'INTEGER(10) UNSIGNED NOT NULL',
            'log_link_visit_action.idaction_event_action' => 'INTEGER(10) UNSIGNED DEFAULT NULL',
            'log_link_visit_action.idaction_event_category' => 'INTEGER(10) UNSIGNED DEFAULT NULL',
            'log_conversion.revenue_discount' => 'float default NULL',
            'log_conversion.revenue' => 'float default NULL',
            'log_conversion.revenue_shipping' => 'float default NULL',
            'log_conversion.revenue_subtotal' => 'float default NULL',
            'log_conversion.revenue_tax' => 'float default NULL',
        );

        if (!array_key_exists($name, $dimensions)) {
            return false;
        }

        return strtolower($dimensions[$name]) === strtolower($version);
    }

    public static function onNoUpdateAvailable($versionsThatWereChecked)
    {
        if (!empty($versionsThatWereChecked)) {
            // invalidate cache only if there were actually file changes before, otherwise we write the cache on each
            // request. There were versions checked only if there was a file change but no update, meaning we can
            // set the cache and declare this state as "no update available".
            self::cacheCurrentDimensionFileChanges();
        }
    }

    private static function getCurrentDimensionFileChanges()
    {
        $files = Filesystem::globr(PIWIK_INCLUDE_PATH . '/plugins/*/Columns', '*.php');

        $times = array();
        foreach ($files as $file) {
            $times[$file] = filemtime($file);
        }

        return $times;
    }

    private static function cacheCurrentDimensionFileChanges()
    {
        $changes = self::getCurrentDimensionFileChanges();
        $persistentCache = new PersistentCache('AllDimensionModifyTime');
        $persistentCache->set($changes);
    }

    private static function getCachedDimensionFileChanges()
    {
        $persistentCache = new PersistentCache('AllDimensionModifyTime');
        if ($persistentCache->has()) {
            return $persistentCache->get();
        }

        return array();
    }

    private static function getVisitDimensions()
    {
        return VisitDimension::getAllDimensions();
    }

    /**
     * @return mixed|Dimension[]
     */
    private static function getActionDimensions()
    {
        return ActionDimension::getAllDimensions();
    }

    /**
     * @return mixed|Dimension[]
     */
    private static function getConversionDimensions()
    {
        return ConversionDimension::getAllDimensions();
    }
}